package service

import (
	"cgs-server/pkg/set"
	"cgs-server/server/cgs/app/models"
	"cgs-server/server/cgs/app/models/dto"
	v1 "cgs-server/server/cgs/app/models/v1"
	"cgs-server/server/cgs/app/models/vo"
	"cgs-server/server/cgs/app/store"
	"cgs-server/server/model"
	"cgs-server/server/system/role"
	"cgs-server/server/upload"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"io"
	"mime/multipart"
	"path/filepath"
	"strconv"
	"strings"
)

type WorksheetInfoSrv interface {
	Save(wg *models.WorksheetInfo, user *model.User) error
	Delete(worksheetId string, user *model.User) error
	Update(wg *models.WorksheetInfo, user *model.User) error
	Get(worksheetId string, user *model.User) (*vo.WorksheetInfo, error)
	ListByAppId(appId string, wsTypes []string, user *model.User) (*vo.WorksheetInfoList, error)

	UploadExcel(file *multipart.FileHeader) (*upload.FileData, error)
	PreviewExcel(filePath string) (*models.ExcelSheetList, error)
	ImportExcel(ex *models.ExcelImport, user *model.User) error
	ExportExcel(param *models.FilterParam, user *model.User) (*excelize.File, string, error)
}

type worksheetInfoSrv struct {
	store store.Factory
}

func (w *worksheetInfoSrv) ExportExcel(param *models.FilterParam, user *model.User) (*excelize.File, string, error) {
	var name string
	wsInfoObjId, err := primitive.ObjectIDFromHex(param.WorksheetId)
	if err != nil {
		return nil, name, err
	}
	worksheetInfo, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: wsInfoObjId}, user, v1.GetOptions{})
	name = worksheetInfo.Name
	filter := param.ResolveFilterParam(*worksheetInfo.Fields)
	listOpts := v1.ListOptions{}
	listOpts.SetFilter(&filter)
	if param.PageNo != nil && param.PageSize != nil {
		listOpts.SetPage(*param.PageNo, *param.PageSize)
	}
	listOpts.SetFindOptions(param.ResolveOptions())
	list, err := w.store.WorksheetRows().List(&models.WorksheetRow{WorksheetId: param.WorksheetId}, user, listOpts)
	if err != nil {
		return nil, "", err
	}

	for _, row := range *list {
		data, err := row.Data.FormatData(worksheetInfo.Fields)
		if err != nil {
			return nil, "", err
		}
		row.Data = data
	}

	// 写入到 excel
	var (
		axis    = ""
		colName = ""
		sheet   = "Sheet1"
	)
	f := excelize.NewFile()
	titleStyle, err := f.NewStyle(`{"font":{"bold": true}}`)
	if err != nil {
		return nil, "", err
	}
	// 设置标题
	for i, field := range *worksheetInfo.Fields {
		index := i + 1
		colName, err = excelize.ColumnNumberToName(index)
		if err != nil {
			return nil, name, err
		}
		axis, err = excelize.JoinCellName(colName, 1)
		if err != nil {
			return nil, name, err
		}
		err = f.SetCellValue(sheet, axis, field.Alia)
		if err != nil {
			return nil, name, err
		}
		err = f.SetCellStyle(sheet, axis, axis, titleStyle)
		if err != nil {
			return nil, "", err
		}
	}

	// 设置数据
	for i, d := range *list {
		rowData := d.Data
		for fi, field := range *worksheetInfo.Fields {
			if v, ok := rowData[field.Name]; ok {
				colName, err = excelize.ColumnNumberToName(fi + 1)
				if err != nil {
					return nil, name, err
				}
				axis, err = excelize.JoinCellName(colName, i+2)
				if err != nil {
					return nil, name, err
				}
				err = f.SetCellValue(sheet, axis, v)
				if err != nil {
					return nil, name, err
				}
			}
		}
	}

	return f, name, nil
}

func (w *worksheetInfoSrv) ImportExcel(ex *models.ExcelImport, user *model.User) error {
	if ex.WorksheetId.IsZero() {
		return errors.New("worksheetIs is required")
	}
	worksheetInfo, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: ex.WorksheetId}, user, v1.GetOptions{})
	if err != nil {
		return err
	}
	fileStore := upload.NewFileStore("local")
	file, err := fileStore.Get(ex.Filepath)
	if err != nil {
		return err
	}

	f, err := excelize.OpenReader(file)
	if err != nil {
		return err
	}

	_, _ = worksheetInfo, f

	data, err := ex.ResolveWorksheetData(f, worksheetInfo.Fields)
	if err != nil {
		return err
	}

	err = w.store.WorksheetRows().BatchAddRow(data, user, v1.CreateOptions{})
	if err != nil {
		return err
	}

	ex.RowList = data

	// 删除excel
	defer func(fileStore upload.FileStore, key string, file io.Reader) {
		if g, ok := file.(io.ReadCloser); ok {
			_ = g.Close()
		}
		_ = fileStore.Delete(key)
	}(fileStore, ex.Filepath, file)
	return nil
}

func (w *worksheetInfoSrv) PreviewExcel(filePath string) (*models.ExcelSheetList, error) {
	fileStore := upload.NewFileStore("local")
	get, err := fileStore.Get(filePath)
	if err != nil {
		return nil, err
	}
	if g, ok := get.(io.ReadCloser); ok {
		defer g.Close()
	}
	f, err := excelize.OpenReader(get)
	if err != nil {
		return nil, err
	}

	var sheetList models.ExcelSheetList
	sList := f.GetSheetList()
	for i, s := range sList {
		sheet := models.ExcelSheet{
			SheetName:   s,
			SheetNumber: i,
		}
		var rowList models.ExcelRowList
		rows, err := f.GetRows(s)
		if err != nil {
			return nil, err
		}

		for i2, r := range rows {
			if i2 == 10 {
				break
			}

			row := models.ExcelRow{
				RowNumber: i2,
			}
			var cellList models.ExcelCellList

			for i3, c := range r {
				cell := models.ExcelCell{
					ColumnNumber: i3,
					Value:        c,
				}
				cellList = append(cellList, &cell)
			}

			row.Cells = cellList
			rowList = append(rowList, &row)
		}

		sheet.Rows = rowList
		sheetList = append(sheetList, &sheet)
	}

	return &sheetList, nil
}

func (w *worksheetInfoSrv) UploadExcel(file *multipart.FileHeader) (*upload.FileData, error) {
	fileExt := filepath.Ext(file.Filename)
	fileType := file.Header.Get("Content-Type")
	if strings.ToLower(fileExt) != ".xls" &&
		strings.ToLower(fileExt) != ".xlsx" {
		return nil, errors.New("only xls, xlsx format is allowed to upload")
	}
	fileStore := upload.NewFileStore("local")
	open, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer open.Close()
	url, err := fileStore.Upload(open, file.Filename, file.Size, fileType)
	if err != nil {
		return nil, err
	}

	fileData := upload.FileData{
		FileName: file.Filename,
		FileSize: file.Size,
		FileType: fileType,
		URL:      url,
	}
	return &fileData, nil
}

func (w *worksheetInfoSrv) ListByAppId(appId string, wsTypes []string, user *model.User) (*vo.WorksheetInfoList, error) {
	opts := v1.ListOptions{}
	opts.AddFilter("appId", appId)
	if len(wsTypes) > 0 {
		types := make([]int64, 0, len(wsTypes))
		for _, wsType := range wsTypes {
			i, err := strconv.ParseInt(wsType, 10, 32)
			if err != nil {
				return nil, err
			}
			types = append(types, i)
		}

		opts.AddFilter("type", bson.M{
			"$in": types,
		})
	} else {
		opts.AddFilter("type", models.WorksheetType)
	}

	return w.store.WorksheetInfo().List(&models.WorksheetInfo{}, user, opts)
}

func (w *worksheetInfoSrv) Get(worksheetId string, user *model.User) (*vo.WorksheetInfo, error) {
	wsObjId, err := primitive.ObjectIDFromHex(worksheetId)
	if err != nil {
		return nil, err
	}
	wsInfo, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: wsObjId}, user, v1.GetOptions{})
	if err != nil {
		return nil, err
	}

	//// 权限控制
	//_, err = getAppAuths(wsInfo.AppId, user)
	//if err != nil {
	//	return nil, err
	//}
	//if !user.HasAuth(wsInfo.AppId, server.EditForm, server.EditForm2, server.EditForm4) && user.HasAuth(wsInfo.AppId, server.EditForm3) {
	//	if wsInfo.Owner != user.GetId() {
	//		return nil, errors.New("have no permission")
	//	}
	//}

	// 查询所有视图
	opts := v1.ListOptions{}
	opts.AddFilter("worksheetId", worksheetId)
	viewList, err := w.store.WorksheetView().List(&models.WorksheetView{}, user, opts)
	if err != nil {
		return nil, err
	}
	wsInfo.ViewList = viewList

	return wsInfo, err
}

func (w *worksheetInfoSrv) Update(wg *models.WorksheetInfo, user *model.User) error {
	if wg.Id.IsZero() {
		return fmt.Errorf("id is rquired")
	}
	wsInfo, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: wg.Id}, user, v1.GetOptions{})
	if err != nil {
		return err
	}

	err = wg.ResolveFields(wsInfo.Fields)
	if err != nil {
		return err
	}
	err = w.store.WorksheetInfo().Update(wg, user, v1.UpdateOptions{})
	if err != nil {
		return err
	}

	// 更新分组中的表单顺序
	if wg.GroupId != "" {
		groupObjId, err := primitive.ObjectIDFromHex(wg.GroupId)
		if err != nil {
			return err
		}
		group, err := w.store.WorksheetGroup().Get(&models.WorksheetGroup{Id: groupObjId}, user, v1.GetOptions{})
		if err != nil {
			return err
		}
		if group.WorksheetInfoIds != nil {
			exist := false
			for _, s := range *group.WorksheetInfoIds {
				if s == wg.GetId() {
					exist = true
				}
			}
			if !exist {
				*group.WorksheetInfoIds = append(*group.WorksheetInfoIds, wg.GetId())
				err = w.store.WorksheetGroup().Update(&dto.WorksheetGroup{
					Id:               groupObjId,
					WorksheetInfoIds: group.WorksheetInfoIds,
				}, user, v1.UpdateOptions{})
				if err != nil {
					return err
				}
			}
		}
	}

	return err
}

func (w *worksheetInfoSrv) Delete(worksheetId string, user *model.User) error {
	// todo 删除表单时要处理一下子表的数据（删除 or 保留）
	wsObjId, err := primitive.ObjectIDFromHex(worksheetId)
	if err != nil {
		return err
	}
	err = w.store.WorksheetInfo().Delete(&models.WorksheetInfo{Id: wsObjId}, user, v1.DeleteOptions{})
	if err != nil {
		return err
	}

	// 删除表单的所有视图
	opts := v1.DeleteOptions{}
	opts.AddFilter("worksheetId", worksheetId)
	err = w.store.WorksheetView().DeleteMany(&models.WorksheetView{}, user, opts)
	if err != nil {
		return err
	}

	err = w.store.WorksheetInfo().Drop(&models.WorksheetInfo{Id: wsObjId})
	return err
}

func (w *worksheetInfoSrv) Save(wg *models.WorksheetInfo, user *model.User) error {
	err := wg.ResolveFields(nil)

	//_, err = getAppAuths(wg.AppId, user)
	//if err != nil {
	//	return err
	//}
	//if !user.HasOneAuth(wg.AppId, server.EditForm, server.EditForm2, server.EditForm3) {
	//	return errors.New("have no permission")
	//}

	if err != nil {
		return err
	}
	err = w.store.WorksheetInfo().Save(wg, user, v1.CreateOptions{})
	if err != nil {
		return err
	}

	// 更新分组中的表单顺序
	groupObjId, err := primitive.ObjectIDFromHex(wg.GroupId)
	if err != nil {
		return err
	}
	group, err := w.store.WorksheetGroup().Get(&models.WorksheetGroup{Id: groupObjId}, user, v1.GetOptions{})
	if err != nil {
		return err
	}
	if group.WorksheetInfoIds != nil {
		*group.WorksheetInfoIds = append(*group.WorksheetInfoIds, wg.GetId())
		err = w.store.WorksheetGroup().Update(&dto.WorksheetGroup{
			Id:               groupObjId,
			WorksheetInfoIds: group.WorksheetInfoIds,
		}, user, v1.UpdateOptions{})
		if err != nil {
			return err
		}
	}

	return nil
}

func newWorksheetInfoSrv(s *service) WorksheetInfoSrv {
	return &worksheetInfoSrv{
		store: s.store,
	}
}

func getAppAuths(visualId string, user *model.User) (bool, error) {
	isOwner := false
	roleService := role.NewRoleService()
	newSet := set.NewSet()
	ids := user.VisualAppMap[visualId]
	if ids == nil {
		return false, nil
	}
	roles, err := roleService.ListByIds(ids, &model.DataUserModel{Data: &model.Role{}})
	if err != nil {
		return false, err
	}

	if user.AppFormAuthType == nil {
		user.AppFormAuthType = make(map[string]int)
	}
	user.AppFormAuthType[visualId] = 0

	// 当前app下的表单权限
	formAuth := make(map[string][]string)
	formAuthSet := make(map[string]set.Set)
	for _, r := range *roles {
		if r.AppAuths != nil {
			for _, auth := range r.AppAuths {
				newSet.Add(auth)
			}
		}
		for s, auths := range r.FormAuths {
			if formAuthSet[s] == nil {
				formAuthSet[s] = set.NewSet()
			}
			for _, s2 := range auths {
				formAuthSet[s].Add(s2)
			}
		}
		if r.FormAuthType != nil && *r.FormAuthType == 1 {
			user.AppFormAuthType[visualId] = 1
		}
	}
	if user.AppAuthSet == nil {
		user.AppAuthSet = make(map[string]set.Set)
	}
	if user.FormAuths == nil {
		user.FormAuths = make(map[string]map[string][]string)
	}
	user.AppAuthSet[visualId] = newSet
	if user.AppAuths[visualId] == nil {
		user.AppAuths[visualId] = make([]string, 0)
	}
	newSet.Each(func(i interface{}) bool {
		user.AppAuths[visualId] = append(user.AppAuths[visualId], i.(string))
		return false
	})

	for k, v := range formAuthSet {
		v.Each(func(a interface{}) bool {
			formAuth[k] = append(formAuth[k], a.(string))
			return false
		})
	}
	user.FormAuths[visualId] = formAuth

	id, err := roleService.FindVisualByVisualId(visualId, "App")
	if err != nil {
		return false, err
	}

	if user.GetId() == id.Owner {
		isOwner = true
	}

	return isOwner, nil
}
